# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: xguo

import time
import coin.exchange.base.kr_rest.level_book as level_book_mdl
import coin.exchange.base.kr_rest.public_client_base as pubcb
import coin.exchange.okex_futures.kr_rest.native_public_client as npubc

from coin.exchange.base.kr_rest.rest_client_base import RestUpdate
from coin.exchange.okex_futures.kr_rest.currency import OkexCurrency
from coin.exchange.okex_futures.kr_rest.futures_constants import (
    active_contract_list,
    contract_type_list,
)
from coin.exchange.okex_futures.kr_rest.futures_product import (
    OkexFuturesProduct,)
from coin.proto.coin_market_query_pb2 import (
    ProductTicker,
    ExchangeTicker,
    ProductKlineElement,
    ProductKline,
)


class OkexFuturesFeedParser(object):
  @staticmethod
  def parse_native_ticker(update_msg, product):
    """Sample ticker response:
    {
      "date": "1519628505",
      "ticker": {
        "high": 9778,
        "vol": 5632958,
        "day_high": 0,
        "last": 9403.02,
        "low": 9232,
        "contract_id": 201803020000034,
        "buy": 9398.87,
        "sell": 9402.97,
        "coin_vol": 0,
        "day_low": 0,
        "unit_amount": 100
      }
    }
    """
    ticker = update_msg['ticker']
    return ProductTicker(
        high=float(ticker['high']),
        low=float(ticker['low']),
        last=float(ticker['last']),
        ask=float(ticker['sell']),
        bid=float(ticker['buy']),
        volume_24hr=float(ticker['vol']),  # Last rolling 24 hours.
        exchange_timestamp=int(float(update_msg['date']) * 1e+9),
        native_symbol=product.native_symbol,
        symbol=product.symbol,
    )

  @staticmethod
  def parse_native_kline(update_msg, product, kline_period):
    """Sample kline response:
    [
        [
            1440308700000,
            233.37,
            233.48,
            233.37,
            233.48,
            52,
            22.2810015
        ],
        [
            1440308760000,
            233.38,
            233.38,
            233.27,
            233.37,
            186,
            79.70234956
        ]
    ]
    """
    klines = update_msg
    kline_group = []
    for kline in klines:
      product_kline = ProductKlineElement(kline_timestamp=int(kline[0] * 1e+6),
                                          open=float(kline[1]),
                                          high=float(kline[2]),
                                          low=float(kline[3]),
                                          close=float(kline[4]),
                                          volume=float(kline[5]),
                                          turnover=float(kline[6]))
      kline_group.append(product_kline)
    return ProductKline(
        symbol=product.symbol,
        native_symbol=product.native_symbol,
        exchange='Okex',
        kline_period=kline_period,
        klines=kline_group,
    )

  @staticmethod
  def parse_level_book(update_msg, product):
    """ BTC-USDT raw orderbook data:
    Both asks and bids are sorted in decending order of the price.
    {
      "asks": [ [ 9410.78, 5 ], [ 9410, 75 ], [ 9409.79, 26 ]],
      "bids": [ [ 9402.11, 1 ], [ 9400.68, 26 ], [ 9400.12, 26 ]]
    }
    """
    level_book = level_book_mdl.LevelBook(product)

    for elem in reversed(update_msg['asks']):
      level_elem = level_book_mdl.LevelElement(float(elem[0]), float(elem[1]), 0)
      level_book.add_ask(level_elem)

    for elem in update_msg['bids']:
      level_elem = level_book_mdl.LevelElement(float(elem[0]), float(elem[1]), 0)
      level_book.add_bid(level_elem)
    level_book.finalize()
    return level_book


class OkexFuturesPublicClient(pubcb.PublicClientBase):
  def __init__(self, timeout=1):
    self.npubc = npubc.OkexFuturesNativePublicClient(timeout=timeout)

  def query_ticker_impl(self, product):
    currency_pair, contract_type = product.get_query_pair()
    update = self.npubc.get_ticker(currency_pair, contract_type)
    update.msg = OkexFuturesFeedParser.parse_native_ticker(update.msg, product)
    return update

  def query_exchange_ticker_impl(self):
    """OkexFutures API does not allow query all tickers for all the products,
    so we need to query one by one.  """
    raw_tickers = []
    product_ticker_list = []
    req_time = time.time()
    for currency_pair, contract_type in active_contract_list:
      for contract_type in contract_type_list:
        product = OkexFuturesProduct.FromCurrencyPairAndContractType(currency_pair, contract_type)
        update = self.npubc.get_ticker(currency_pair, contract_type)
        raw_tickers.append(update.msg)
        product_ticker_list.append(OkexFuturesFeedParser.parse_native_ticker(update.msg, product))
    res_time = time.time()

    exchange_ticker = ExchangeTicker(exchange='Okex', each_ticker=product_ticker_list)
    update = RestUpdate(req_time, res_time, raw_tickers)
    update.msg = exchange_ticker
    return update

  def query_history_kline_impl(self, product, kline_period, size):
    currency_pair, contract_type = product.get_query_pair()
    update = self.npubc.get_history_kline(currency_pair, kline_period, contract_type, size)
    update.msg = OkexFuturesFeedParser.parse_native_kline(update.msg, product, kline_period)
    return update

  def query_level_book_impl(self, product):
    currency_pair, contract_type = product.get_query_pair()
    update = self.npubc.get_orderbook(currency_pair, contract_type)
    update.msg = OkexFuturesFeedParser.parse_level_book(update.msg, product)
    return update
